package com.robot.console.util;

import org.apache.log4j.Logger;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.filter.ContentFilter;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

import java.io.*;
import java.net.URL;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

public class XmlTools {
	final static Logger logger = Logger.getLogger(XmlTools.class);
	private final static ContentFilter contentFilter = new ContentFilter(
			ContentFilter.COMMENT);

	public static SAXBuilder getSAXBuilder() throws IllegalAccessException {
		SAXBuilder builder = null;
		Thread currentThread = Thread.currentThread();
		if (currentThread instanceof ContextThread) {
			ContextThread currentContextThread = (ContextThread) currentThread;
			builder = (SAXBuilder) currentContextThread
					.getContext("saxbuilder");
			if (builder == null) {
				builder = new SAXBuilder(
						"org.apache.crimson.parser.XMLReaderImpl", false);
				currentContextThread.addContext("saxbuilder", builder);
			}
		} else {
			builder = new SAXBuilder("org.apache.crimson.parser.XMLReaderImpl",
					false);
		}
		return builder;
	}

	/**
	 * Convert a xml String to Document object
	 * 
	 * @param xmlStr
	 *            -- xml String
	 * @return Document
	 */
	public static Document string2Document(String xmlStr) {
		Document doc = null;
		try {
			doc = getSAXBuilder().build(xmlStr);
		} catch (Exception ex) {
			return null;
		}
		return doc;
	}

	/**
	 * Convert a xml byte array to Document object
	 * 
	 * @param buf
	 *            -- xml byte array
	 * @return Document
	 */
	public static Document byte2Document(byte[] buf) {
		ByteArrayInputStream bais = new ByteArrayInputStream(buf);
		try {
			Document xmlDoc = getSAXBuilder().build(bais);
			bais.close();
			return xmlDoc;
		} catch (Exception ex) {
			return null;
		}
	}

	/**
	 * Convert a xml Document object to a String
	 * 
	 * @param xmlDoc
	 *            -- xml Document object
	 * @return String
	 */
	public static String document2String(Document xmlDoc) {
		ByteBuffer buf = XmlTools.doucment2ByteBuffer(xmlDoc, false);
		String xmlSTR = "";
		try {
			xmlSTR = new String(buf.array(), "UTF-8");
		} catch (UnsupportedEncodingException ex) {
			xmlSTR = new String(buf.array());
		}
		return xmlSTR;
	}

	/**
	 * Write the xml document object to a file
	 * 
	 * @param xmlDoc
	 *            -- xml document object filename -- absoulte file path eg:
	 *            c:\default.xml
	 * @return boolean
	 */
	public static boolean document2File(Document xmlDoc, String filename) {
		try {
			Format f = Format.getPrettyFormat();
			f.setExpandEmptyElements(true);
			f.setLineSeparator(System.getProperty("line.separator"));
			XMLOutputter outputter = new XMLOutputter(f);
			FileOutputStream fos = new FileOutputStream(filename);
			outputter.output(xmlDoc, fos);
			fos.close();
			return true;
		} catch (IOException ex) {
			XmlTools.logger.error("Error to write xml file " + filename
					+ " -- " + ex.getMessage());
			return false;
		}
	}

	/**
	 * Read a xml file and convert it to a xml document object
	 * 
	 * @param fp
	 *            -- File
	 * @return Document
	 */
	public static Document file2Document(File fp) throws Exception {
		Document xmlDoc = null;
		try {
			FileInputStream in = new FileInputStream(fp);
			int len = (int) fp.length();
			int flen = 3;
			byte[] tmp = new byte[flen];
			in.read(tmp);
			byte[] header = {-17, -69, -65};
			if ((new String(tmp)).equals(new String(header))) {
				byte[] buf = new byte[len - flen];
				in.read(buf);
				in.close();
				String filename = fp.getPath();
				OutputStream out = new FileOutputStream(filename, false);
				OutputStream outBuffer = new BufferedOutputStream(out);
				out.write(buf);
				out.close();
				outBuffer.close();
				File newfp = new File(filename);
				xmlDoc = getSAXBuilder().build(newfp);
			} else {
				in.close();
				xmlDoc = getSAXBuilder().build(fp);
			}
		} catch (Exception ex) {
			xmlDoc = fileRemoveComment2Document(fp);
			if (xmlDoc == null) {
				throw ex;
			}
		}
		return xmlDoc;
	}

	/**
	 * Convert a xml document object to a ByteBuffer
	 * 
	 * @param xmlDoc
	 *            -- xml document object isAddEnd -- true or false, if true it
	 *            will add an end symbol byte 0, the reason of add an end
	 *            symbol, please reference the XMLSocket Manual of flash
	 * @return ByteBuffer
	 */
	public static ByteBuffer doucment2ByteBuffer(Document xmlDoc,
			boolean isAddEnd) {
		try {
			ByteArrayOutputStream bao = new ByteArrayOutputStream();
			Format f = Format.getCompactFormat();
			f.setExpandEmptyElements(true);
			f.setLineSeparator("");
			XMLOutputter outputter = new XMLOutputter(f);
			outputter.output(xmlDoc, bao);
			byte[] b = bao.toByteArray();
			ByteBuffer buf;
			if (isAddEnd) {
				buf = ByteBuffer.allocate(b.length + 1);
				buf.put(b);
				buf.put((byte) 0);
			} else {
				buf = ByteBuffer.allocate(b.length);
				buf.put(b);
			}
			buf.rewind();
			return buf;
		} catch (IOException ex) {
			return null;
		}
	}

	public static ByteBuffer element2ByteBuffer(Element e, boolean isAddEnd) {
		try {
			ByteArrayOutputStream bao = new ByteArrayOutputStream();
			Format f = Format.getCompactFormat();
			f.setExpandEmptyElements(true);
			f.setLineSeparator("");
			XMLOutputter outputter = new XMLOutputter(f);
			outputter.output(e, bao);
			byte[] b = bao.toByteArray();
			ByteBuffer buf;
			if (isAddEnd) {
				buf = ByteBuffer.allocate(b.length + 1);
				buf.put(b);
				buf.put((byte) 0);
			} else {
				buf = ByteBuffer.allocate(b.length);
				buf.put(b);
			}
			buf.rewind();
			return buf;
		} catch (IOException ex) {
			return null;
		}
	}

	public static String getTimeStr(long time) {
		SimpleDateFormat output_df = new SimpleDateFormat(
				"yyyy-MM-dd hh:mm:ss a");
		Date d = new Date(time);
		return output_df.format(d);
	}

	public static boolean getInList(String liststr, String str) {
		String newstr = str.toUpperCase();
		String[] lists = liststr.split(",");
		boolean find = false;
		for (int i = 0; i < lists.length; i++) {
			if (lists[i].toUpperCase().equals(newstr)) {
				find = true;
				break;
			}
		}
		return find;
	}

	/**
	 * remove the comment element from the xml
	 */
	@SuppressWarnings("rawtypes")
	public static Element removeComment(Element element) {
		element.removeContent(contentFilter);
		List children = element.getChildren();
		for (Iterator it = children.iterator(); it.hasNext();) {
			Element e = (Element) it.next();
			if (e.getChildren().size() > 0) {
				removeComment(e);
			}
		}
		return element;
	}

	/**
	 * Convert a url stream to a xml document
	 * 
	 * @param urlstr
	 *            -- url of the xml content
	 * @return Document
	 */
	public static Document url2Document(String urlstr) throws Exception {
		Document xmlDoc = null;
		URL url = new URL(urlstr);
		xmlDoc = getSAXBuilder().build(url);
		return xmlDoc;
	}

	public static Document fileRemoveComment2Document(File file) {
		Document xmlDoc = null;
		try {
			String strContent = FileHandler.readFileToString(file, true);
			if (strContent.length() > 0) {
				int startTag = 0;
				int endTag = 0;
				do {
					startTag = strContent.indexOf("<!--");
					endTag = strContent.indexOf("-->");
					if (startTag > -1 && endTag > -1) {
						String block1 = strContent.substring(0, startTag);
						String block2 = strContent.substring(endTag + 3);
						strContent = block1 + block2;
					}
				} while (startTag > -1 && endTag > -1);
				xmlDoc = byte2Document(strContent.getBytes());
				if (xmlDoc != null) {
					document2File(xmlDoc, file.getAbsolutePath());
				}
			}
		} catch (Exception ex) {
			xmlDoc = null;
		}
		return xmlDoc;
	}

	public static ByteBuffer doucment2ByteBuffer(Document xmlDoc,
			byte[] shareBytes, int tailLength, boolean isAddEnd) {
		try {
			ByteArrayOutputStream bao = new ByteArrayOutputStream();
			Format f = Format.getCompactFormat();
			f.setExpandEmptyElements(true);
			f.setLineSeparator("");
			XMLOutputter outputter = new XMLOutputter(f);
			outputter.output(xmlDoc.getRootElement(), bao);
			byte[] b = bao.toByteArray();
			int shareBytesLength = shareBytes.length;
			int bLength = b.length;
			int offset = bLength - tailLength;
			ByteBuffer buf;
			if (isAddEnd) {
				buf = ByteBuffer.allocate(bLength + shareBytesLength + 1);
				buf.put(b, 0, offset);
				buf.put(shareBytes);
				buf.put(b, offset, tailLength);
				buf.put((byte) 0);
			} else {
				buf = ByteBuffer.allocate(bLength + shareBytesLength);
				buf.put(b, 0, offset);
				buf.put(shareBytes);
				buf.put(b, offset, tailLength);
			}
			buf.rewind();
			return buf;
		} catch (IOException ex) {
			return null;
		}
	}

	public static ByteBuffer json2ByteBuffer(String json, boolean isAddEnd) {
		try {
			byte[] b = json.getBytes();
			ByteBuffer buf;
			if (isAddEnd) {
				buf = ByteBuffer.allocate(b.length + 1);
				buf.put(b);
				buf.put((byte) 0);
			} else {
				buf = ByteBuffer.allocate(b.length);
				buf.put(b);
			}
			buf.rewind();
			return buf;
		} catch (Exception ex) {
			return null;
		}
	}
}
